package system

import (
	"context"
	"gf_blog_admin/internal/consts"
	"gf_blog_admin/internal/dao"
	"gf_blog_admin/internal/model"
	"gf_blog_admin/internal/service"
	"gf_blog_admin/utility/casbinPolicy"
	"gf_blog_admin/utility/crypto"
	"gf_blog_admin/utility/utils"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/errors/gerror"
)

type sSysUser struct{}

func init() {
	service.RegisterSysUser(NewSysUser())
}

func NewSysUser() *sSysUser {
	return &sSysUser{}
}

func (s *sSysUser) List(ctx context.Context, in *model.UserListInput) (out []*model.UserListItem, total int, err error) {
	var (
		cls = dao.BlSysUser.Columns()
		m   = dao.BlSysUser.Ctx(ctx)
	)

	if in.Username != "" {
		m = m.WhereLike(cls.Username, in.Username+"%")
	}
	if in.Nickname != "" {
		m = m.WhereLike(cls.Nickname, in.Nickname+"%")
	}
	if in.Status != "" {
		m = m.Where(cls.UserStatus, utils.StrToInt(in.Status))
	}

	out = make([]*model.UserListItem, 0)
	err = m.Fields(
		cls.Id, cls.Username, cls.Nickname, cls.Sex, cls.Mobile, cls.Email,
		cls.Avatar, cls.UserStatus, cls.CreatedAt,
	).
		Page(in.Page, in.PageSize).
		ScanAndCount(&out, &total, false)
	return
}

func (s *sSysUser) Detail(ctx context.Context, id uint64) (user *model.UserItem, err error) {
	cls := dao.BlSysUser.Columns()
	err = dao.BlSysUser.Ctx(ctx).Where(cls.Id, id).Scan(&user)
	if err != nil {
		return nil, err
	}
	if user == nil {
		return nil, gerror.New("用户记录不存在")
	}

	var role []string
	role, err = s.getRoles(user.Username)
	if err != nil {
		return nil, err
	}
	if role != nil && len(role) > 0 {
		user.Role = role[0]
	}
	return
}

func (s *sSysUser) Store(ctx context.Context, in *model.UserFormInput) (err error) {
	cls := dao.BlSysUser.Columns()

	if err = utils.IsUnique(ctx, &dao.BlSysUser, map[string]string{
		cls.Username: in.Username,
	}, "用户名已存在"); err != nil {
		return err
	}

	if in.PasswordHash == "" {
		in.PasswordHash = in.Username
	}
	in.PasswordHash = crypto.PasswordHash(in.PasswordHash, 10)
	err = dao.BlSysUser.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		if _, err = dao.BlSysUser.Ctx(ctx).Data(in).OmitEmptyData().Insert(); err != nil {
			return err
		}
		if err = s.attachRoles(in.Username, []string{in.Role}); err != nil {
			return err
		}
		return nil
	})
	return
}

func (s *sSysUser) Update(ctx context.Context, id uint64, in *model.UserFormInput) (err error) {
	cls := dao.BlSysUser.Columns()

	if err = utils.IsUnique(ctx, &dao.BlSysUser, map[string]interface{}{
		cls.Id + " !=": id,
		cls.Username:   in.Username,
	}, "用户名已存在"); err != nil {
		return err
	}

	user, err := s.Detail(ctx, id)
	if err != nil {
		return err
	}
	if user.Username == consts.SuperAdmin && in.Username != user.Username {
		return gerror.New("超管用户禁止修改用户名")
	}
	if in.PasswordHash != "" {
		in.PasswordHash = crypto.PasswordHash(in.PasswordHash, 10)
	}
	err = dao.BlSysUser.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		if _, err = dao.BlSysUser.Ctx(ctx).Where(cls.Id, id).Data(in).Update(); err != nil {
			return err
		}
		if err = s.assignRoles(in.Username, []string{in.Role}); err != nil {
			return err
		}
		return nil
	})
	return
}

func (s *sSysUser) Destroy(ctx context.Context, ids []uint64) error {
	var (
		users []string
		cls   = dao.BlSysUser.Columns()
		m     = dao.BlSysUser.Ctx(ctx)
	)

	ids = utils.UniqueIds(ids)
	result, err := m.WhereIn(cls.Id, ids).Fields(cls.Username).Array()
	if err != nil {
		return err
	}
	if result == nil || len(result) == 0 {
		return gerror.New("数据记录不存在")
	}
	for _, v := range result {
		va := v.String()
		if va == consts.SuperAdmin {
			return gerror.New("超管用户禁止删除！")
		}
		users = append(users, va)
	}

	err = dao.BlSysUser.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		if _, err = m.WhereIn(cls.Id, ids).Delete(); err != nil {
			return err
		}
		if err = s.detachRoles(users); err != nil {
			return err
		}
		return nil
	})
	return err
}

func (s *sSysUser) ResetPwd(ctx context.Context, id uint64, password string) (err error) {
	var (
		cls = dao.BlSysUser.Columns()
		m   = dao.BlSysUser.Ctx(ctx)
	)
	if _, err = s.Detail(ctx, id); err != nil {
		return err
	}

	err = dao.BlSysUser.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		_, err = m.Where(cls.Id, id).Data(map[string]string{
			cls.PasswordHash: crypto.PasswordHash(password, 10),
		}).Update()
		if err != nil {
			return gerror.Wrap(err, "用户密码重置失败，请稍后重试！")
		}
		return nil
	})
	return
}

func (s *sSysUser) getRoles(user string) ([]string, error) {
	return casbinPolicy.Enforcer.GetRolesForUser(user)
}

func (s *sSysUser) attachRoles(user string, roles []string) error {
	if user == "" || roles == nil || len(roles) == 0 || roles[0] == "" {
		return nil
	}
	_, err := casbinPolicy.Enforcer.AddRolesForUser(user, roles)
	return err
}

func (s *sSysUser) assignRoles(user string, roles []string) error {
	err := s.detachRole(user)
	if err != nil {
		return err
	}
	err = s.attachRoles(user, roles)
	return err
}

func (s *sSysUser) detachRole(user string) error {
	_, err := casbinPolicy.Enforcer.DeleteUser(user)
	return err
}

func (s *sSysUser) detachRoles(users []string) error {
	for _, user := range users {
		if err := s.detachRole(user); err != nil {
			return err
		}
	}
	return nil
}
